package ph.com.gs3.formalistics.presenter;

import ph.com.gs3.formalistics.R;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.communicators.HttpCommunicatorModule.ResultCode;
import ph.com.gs3.formalistics.model.dao.UserAccountsDAO;
import ph.com.gs3.formalistics.model.valueobjects.business.User;
import ph.com.gs3.formalistics.presenter.fragments.views.LoginViewFragment;
import ph.com.gs3.formalistics.presenter.fragments.views.SplashViewFragment;
import ph.com.gs3.formalistics.service.managers.SessionManager.LoginResult;
import ph.com.gs3.formalistics.service.services.AuthenticationService;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class LoginActivity extends Activity implements LoginViewFragment.LoginActionsListener {

	public static final String TAG = LoginActivity.class.getSimpleName();

	// UI Fragments
	private LoginViewFragment loginFragment;
	private SplashViewFragment splashFragment;

	// Data Access Objects
	private UserAccountsDAO userAccountsDAO;

	public static final String FRAGMENT_TITLE_LOGIN = "Login to Formalistics";
	public static final String FRAGMENT_TITLE_SPLASH = "Formalistics";

	public static enum Status {
		NEEDS_REAUTHENTICATION, APPLICATION_ERROR, NORMAL
	}

	public static final String EXTRA_STATUS = "status";
	public static final String EXTRA_LOGIN_RESULT = "login_result";

	private boolean authenticationServiceRegistered = false;

	// ========================================================================
	// Extra Fields

	private Status status;
	private LoginResult loginResult;

	// ========================================================================
	// View Methods

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_login);

		initializeExtras();

		// initialize the fragments
		loginFragment = new LoginViewFragment();
		splashFragment = new SplashViewFragment();

		// initially show splash view
		setFragment(splashFragment, FRAGMENT_TITLE_SPLASH);

		// have the application decide if it needs to show the login view or it can
		// directly go to the main view
		navigateOrShowLogin();

	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		unregisterBroadcastReceivers();
	}

	// ========================================================================
	// {{ Functional Methods

	private void initializeExtras() {
		Bundle extras = getIntent().getExtras();

		status = Status.NORMAL;
		loginResult = null;

		if (extras != null) {
			if (extras.containsKey(EXTRA_STATUS)) {
				status = (Status) extras.get(EXTRA_STATUS);
			}

			if (extras.containsKey(EXTRA_LOGIN_RESULT)) {
				loginResult = (LoginResult) extras.get(EXTRA_LOGIN_RESULT);
			}

		}
	}

	public void setFragment(Fragment fragment, String title) {

		FragmentTransaction transaction = getFragmentManager().beginTransaction();
		transaction.replace(R.id.container, fragment);

		transaction.commit();
		// transaction.commitAllowingStateLoss();
		setTitle(title);
	}

	private void navigateOrShowLogin() {

		// lazy load UserAccountsDAO object
		if (userAccountsDAO == null) {
			userAccountsDAO = new UserAccountsDAO(LoginActivity.this);
		}

		userAccountsDAO.open();
		User activeUser = userAccountsDAO.getActiveUserAccount();
		userAccountsDAO.close();

		if (activeUser != null && status == Status.NORMAL) {
			// TODO: check if the active user can be passed to the main activity instead
			// of having it lookup again.
			navigateToMainActivity(activeUser);
		} else {
			showLoginFragment();
		}

	}

	private void showLoginFragment() {

		setFragment(loginFragment, FRAGMENT_TITLE_LOGIN);

		// check if this method is called with the need to re-authenticate
		if (status != Status.NORMAL && loginResult != null) {
			loginFragment.setErrorMessage(loginResult.getMessage());
		}

		registerBroadcastReceivers();

	}

	private void navigateToMainActivity(User activeUser) {

		// start the main activity and close this activity
		Intent mainActivity = new Intent(LoginActivity.this, MainActivity.class);
		mainActivity.putExtra(MainActivity.EXTRA_LOGGED_IN_USER, activeUser);
		mainActivity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
		startActivity(mainActivity);
		finish();

	}

	private void registerBroadcastReceivers() {
		// register forms update broadcast receiver

		IntentFilter authenticationBroadcastReceiverFilter = new IntentFilter();

		authenticationBroadcastReceiverFilter
		        .addAction(AuthenticationService.ACTION_ON_LOGIN_SERVICE_DONE);
		authenticationBroadcastReceiverFilter.addCategory(Intent.CATEGORY_DEFAULT);
		registerReceiver(authenticationBroadcastListener, authenticationBroadcastReceiverFilter);

		authenticationServiceRegistered = true;

	}

	private void unregisterBroadcastReceivers() {

		if (authenticationServiceRegistered) {
			unregisterReceiver(authenticationBroadcastListener);
			authenticationServiceRegistered = false;
		}

	}

	private void startAuthenticationService(String server, String email, String password) {
		Intent authenticationServiceIntent = new Intent(LoginActivity.this,
		        AuthenticationService.class);

		authenticationServiceIntent.putExtra(AuthenticationService.EXTRA_SERVER, server);
		authenticationServiceIntent.putExtra(AuthenticationService.EXTRA_EMAIL, email);
		authenticationServiceIntent.putExtra(AuthenticationService.EXTRA_PASSWORD, password);

		startService(authenticationServiceIntent);
	}

	// }}

	// ========================================================================
	// Implementation Methods

	@Override
	public void onSignIn(String server, String email, String password) {

		if (splashFragment == null) {
			splashFragment = new SplashViewFragment();
		}

		setFragment(splashFragment, FRAGMENT_TITLE_SPLASH);
		splashFragment.setLoadingMessage("Logging in to server");

		startAuthenticationService(server, email, password);
	}

	// ========================================================================
	// Service Broadcast Listeners

	private BroadcastReceiver authenticationBroadcastListener = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {

			String action = intent.getAction();

			if (AuthenticationService.ACTION_ON_LOGIN_SERVICE_DONE.equals(action)) {

				Bundle extras = intent.getExtras();
				LoginResult result = (LoginResult) extras
				        .getSerializable(AuthenticationService.EXTRA_LOGIN_RESULTS);

				loginResult = result;

				if (ResultCode.SUCCESS.equals(result.getLoginResultCode())) {
					User activeUser = result.getActiveUser();
					navigateToMainActivity(activeUser);
				} else {

					String message = null;
					switch (result.getLoginResultCode()) {
					// TODO: write the messages on xml instead of here
						case APPLICATION_ERROR:
							message = "The application encountered an unexpected error, please try again later.";
							break;
						case CONNECTION_FAILED:
							message = "Unable to connect to server, check your connection or contact your administrator";
							break;
						case INVALID_SERVER_RESPONSE:
							message = "The server gave an invalid response, please contact your administrator";
							break;
						case LOGIN_FAILED:
							message = result.getMessage();
							break;
						default:
							break;
					}

					setFragment(loginFragment, FRAGMENT_TITLE_LOGIN);
					loginFragment.setErrorMessage(message);

				}

			} else {
				FLLogger.d(TAG, "AuthenticationService unhandled action: " + action);
			}

		}

	};

}
